#!/usr/bin/env node
const path = require('path')
const { Readable } = require('stream')
const Koa = require('koa')
const send = require('koa-send')
const compilerSFC = require('@vue/compiler-sfc')

const app = new Koa()

let n = 0
// 将ctx.body的流转换成字符串
// stream读取的流，是一个异步的过程
const streamToString = stream => new Promise((resolve, reject) => {
  const chunks = []
  // 监听读取
  stream.on('data', chunk => chunks.push(chunk))
  // 读取完毕
  // Buffer.concat(chunks).toString('utf-8') 合并转换成字符串
  stream.on('end', () => resolve(Buffer.concat(chunks).toString('utf-8')))
  // 出错
  stream.on('error', reject)
})

// 将字符串变成流
const stringToStream = text => {
  const stream = new Readable()
  stream.push(text)
  stream.push(null)
  return stream
}

// 当请求当路径是@module开头，进行特殊处理
// 3. 加载第三方模块
app.use(async (ctx, next) => {
  // ctx.path --> /@modules/vue，以这个开头
  if (ctx.path.startsWith('/@modules/')) {
    // 获取模块当名称
    const moduleName = ctx.path.substr(10)
    // 用path获取正确的路径，将其指向node_modules的package.json，package.json中指向入口在哪里
    const pkgPath = path.join(process.cwd(), 'node_modules', moduleName, 'package.json')
    // 加载package.json
    const pkg = require(pkgPath)
    // /@modules是我们修改后的路径是不存在的，我们拦截到/@modules以后要将其路径改成正确的 node_modules
    ctx.path = path.join('/node_modules', moduleName, pkg.module)
  }
  await next()
})

// 1. 静态文件服务器
app.use(async (ctx, next) => {
  // 返回静态文件index.html 
  await send(ctx, ctx.path, { root: process.cwd(), index: 'index.html' })
  await next()
})

// 4. 处理单文件组件
// 在2之前，因为vue中也可能会加载第三方模块
app.use(async (ctx, next) => {
  // 请求是以.vue结尾
  if (ctx.path.endsWith('.vue')) {
    // 拿到浏览器请求到到数据流，将其转换成字符串
    const contents = await streamToString(ctx.body)
    // compilerSFC vue的编译模块。编译后的结果
    const { descriptor } = compilerSFC.parse(contents)
    let code
    // main.js中没有带type
    if (!ctx.query.type) {
      // 编译后到代码
      code = descriptor.script.content
      // console.log(code)

      // 将编译后的代码改造成vite的样子
      code = code.replace(/export\s+default\s+/g, 'const __script = ')
      code += `
      import { render as __render } from "${ctx.path}?type=template"
      __script.render = __render
      export default __script
      `
    } else if (ctx.query.type === 'template') {
      //  compilerSFC.compileTemplate：编译模版
      // source: descriptor.template.content：
      // descriptor.template.content：获取组件中的模版
      const templateRender = compilerSFC.compileTemplate({ source: descriptor.template.content })
      // 将App.vue编译成render函数返回给浏览器
      code = templateRender.code
    }
    // 因为2中有判断是不是javascript
    ctx.type = 'application/javascript'
    // 将字符串变成流，因为浏览器正常就是接收一个流的形式的内容
    ctx.body = stringToStream(code)
  }
  await next()
})

// 2. 修改第三方模块的路径,将其改成import { createApp } from '/@modules/vue'
app.use(async (ctx, next) => {
  // 判断响应头中的type是不是 javascript
  if (ctx.type === 'application/javascript') {
    // ctx.body是一个流，
    // 将浏览器请求到的数据流转换成字符串
    const contents = await streamToString(ctx.body)

    // import vue from 'vue'
    // import App from './App.vue'
    // 将转换成字符串的contents将其中的请求第三方模块的地址改成 '/@modules/vue'
    ctx.body = contents
      .replace(/(from\s+['"])(?![\.\/])/g, '$1/@modules/')
      // 去掉环境变量的判断
      .replace(/process\.env\.NODE_ENV/g, '"development"')
  }
})

app.listen(3000)
console.log('Server running @ http://localhost:3000')